package aceim.app.dataentity; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import aceim.api.dataentity.Buddy; import aceim.api.dataentity.BuddyGroup; import aceim.api.dataentity.ConnectionState; import aceim.api.dataentity.Entity; import aceim.api.dataentity.EntityWithID; import aceim.api.dataentity.MultiChatRoom; import aceim.api.dataentity.OnlineInfo; import aceim.api.service.AccountService; import aceim.api.service.ApiConstants; import aceim.api.utils.Logger; import aceim.api.utils.Logger.LoggerLevel; import aceim.api.utils.Utils; import android.os.Parcel; import android.os.Parcelable; /** * Account view part, for using in views. * * @author Sergiy Plygun * */ public class Account extends Entity implements EntityWithID { private final List<Buddy> noGroupBuddies = Collections.synchronizedList(new ArrayList<Buddy>()); /** * "Account enabled" flag */ private boolean isEnabled = true; /** * Protocol name (ICQ, XMPP etc) */ private final String protocolName; /** * Protocol service name, which processes this account */ private final String protocolServicePackageName; /** * Protocol-specific identifier (444555666 for ICQ, user@server.com for XMPP * and so on) */ private final String protocolUid; private final OnlineInfo onlineInfo; /** * Buddy group list. */ private final List<BuddyGroup> buddyGroupList = Collections.synchronizedList(new ArrayList<BuddyGroup>()); /** * Buddies with unread messages temporary storage. Buddy uid - number of * unread messages. Non-serializable. */ private Map<String, Byte> unreadsMap = new HashMap<String, Byte>(); /** * Undeletable buddies temporary storage. Non-serializable. */ private List<Buddy> undeletable; /** * Account connection state. */ private ConnectionState connectionState = ConnectionState.DISCONNECTED; @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(protocolServicePackageName); dest.writeString(protocolName); dest.writeString(protocolUid); dest.writeParcelable(onlineInfo, flags); synchronized (buddyGroupList) { dest.writeList(buddyGroupList); } synchronized (noGroupBuddies) { dest.writeList(noGroupBuddies); } dest.writeParcelable(connectionState, flags); dest.writeByte((byte) (isEnabled ? 1 : 0)); } public static final Parcelable.Creator<Account> CREATOR = new Parcelable.Creator<Account>() { @Override public Account createFromParcel(Parcel source) { // Omitting classname variable used for class hierarchy parcelable support source.readString(); return new Account(source); } @Override public Account[] newArray(int size) { return new Account[size]; } }; @SuppressWarnings("unchecked") private Account(Parcel in) { super(in); protocolServicePackageName = in.readString(); protocolName = in.readString(); protocolUid = in.readString(); onlineInfo = in.readParcelable(OnlineInfo.class.getClassLoader()); buddyGroupList.addAll(in.readArrayList(BuddyGroup.class.getClassLoader())); noGroupBuddies.addAll(in.readArrayList(Buddy.class.getClassLoader())); connectionState = in.readParcelable(ConnectionState.class.getClassLoader()); isEnabled = in.readByte() != 0; } public Account(byte serviceId, String protocolUid, String protocolName, String protocolServiceClassName) { super(serviceId); this.protocolUid = protocolUid; this.protocolName = protocolName; this.protocolServicePackageName = protocolServiceClassName; this.onlineInfo = new OnlineInfo(serviceId, protocolUid); } /** * Find buddy by protocol uid in this account. * * @param uid * input uid * @return buddy or null */ public Buddy getBuddyByProtocolUid(String uid) { for (Buddy buddy : getBuddyList()) { if (uid.equals(buddy.getProtocolUid())) { return buddy; } } return null; } /** * Find buddy group by protocol uid in this account. * * @param id * group id to find * @return group or null */ public BuddyGroup getBuddyGroupByGroupId(String id) { synchronized (buddyGroupList) { for (BuddyGroup group : buddyGroupList) { if (group.getId().equals(id)) { return group; } } } return null; } public int getUnreadMessages() { int unread = 0; for (Buddy buddy : getBuddyList()) { unread += buddy.getUnread(); } return unread; } /** * Merge existing buddy with new values * * @param newBuddy * container with new values for buddy * @param updateStatus * if true, buddy status will also be updated. * @return merged buddy */ public Buddy editBuddy(Buddy newBuddy, boolean updateStatus) { Buddy buddy = getBuddyByProtocolUid(newBuddy.getProtocolUid()); if (buddy != null) { buddy.merge(newBuddy); if (!buddy.getGroupId().equals(newBuddy.getGroupId())) { BuddyGroup oldGroup = getBuddyGroupByGroupId(buddy.getGroupId()); BuddyGroup newGroup = getBuddyGroupByGroupId(newBuddy.getGroupId()); buddy.setGroupId(newBuddy.getGroupId()); oldGroup.getBuddyList().remove(buddy); newGroup.getBuddyList().add(buddy); } } return buddy; } /** * Returns account id, in form of "123456789 ICQ" * * @return protocolUid+" "+protocolName */ public String getAccountId() { return Utils.escapeGeneralDividers(protocolUid) + ApiConstants.GENERAL_DIVIDER + Utils.escapeGeneralDividers(protocolName); } /** * Tell account that it has been disconnected to perform appropriate actions * (reset buddies' state etc...) */ public void disconnected() { for (Buddy buddy : getBuddyList()) { buddy.getOnlineInfo().getFeatures().remove(ApiConstants.FEATURE_STATUS); } connectionState = ConnectionState.DISCONNECTED; } /** * Remove all buddies from account * * @param keepNotInList * do not remove buddies that marked with * {@link AccountService#NOT_IN_LIST_GROUP_ID}, as well as group * chat records. */ public void removeAllBuddies(boolean keepNotInList) { undeletable = new LinkedList<Buddy>(); for (Buddy bu : getBuddyList()) { if (bu.getUnread() > 0) { unreadsMap.put(bu.getProtocolUid(), bu.getUnread()); } if ((bu.getGroupId().equals(ApiConstants.NOT_IN_LIST_GROUP_ID) && keepNotInList) || bu instanceof MultiChatRoom) { undeletable.add(bu); } } synchronized (noGroupBuddies) { noGroupBuddies.clear(); } synchronized (buddyGroupList) { buddyGroupList.clear(); } } /** * Remove buddy from uid. * * @param buddy */ public void removeBuddyByUid(String buddyUid) { synchronized (buddyGroupList) { for (BuddyGroup group : buddyGroupList) { List<Buddy> buddyList = group.getBuddyList(); synchronized (buddyList) { for (Iterator<Buddy> i = buddyList.iterator(); i.hasNext();) { Buddy b = i.next(); if (b.getProtocolUid().equals(buddyUid)) { i.remove(); } } } } } synchronized (noGroupBuddies) { for (Iterator<Buddy> i = noGroupBuddies.iterator(); i.hasNext();) { Buddy b = i.next(); if (b.getProtocolUid().equals(buddyUid)) { i.remove(); } } } } /** * Find buddy by it's internal id. * * @param id * @return buddy, if found, or null. */ public Buddy getBuddyByBuddyId(int id) { for (Buddy buddy : getBuddyList()) { if (buddy.getId() == id) { return buddy; } } return null; } /** * Add buddy to account's buddy list, according to group mark within buddy * * @param buddy */ public void addBuddyToList(Buddy buddy) { // removeBuddyByUid(buddy); if (getBuddyByProtocolUid(buddy.getProtocolUid()) != null) { Logger.log("Buddy " + buddy.getProtocolUid() + " already exists in " + protocolUid, LoggerLevel.INFO); return; } List<Buddy> target = null; synchronized (buddyGroupList) { for (BuddyGroup group : buddyGroupList) { if (group.getId().equals(buddy.getGroupId())) { target = group.getBuddyList(); break; } } } if (target == null) { target = noGroupBuddies; } target.add(buddy); } /** * Edit group. * * @param newGroup * a group's new data holder. */ public void editGroup(BuddyGroup newGroup) { synchronized (buddyGroupList) { for (BuddyGroup group : buddyGroupList) { if (group.getId().equals(newGroup.getId())) { group.setName(newGroup.getName()); break; } } } } /** * Remove buddy group. * * @param group */ public void removeGroup(BuddyGroup group) { synchronized (buddyGroupList) { for (Iterator<BuddyGroup> i = buddyGroupList.iterator(); i.hasNext();) { BuddyGroup bg = i.next(); if (bg.getId().equals(group.getId())) { i.remove(); break; } } } } /** * Merge account with a new data. * * @param origin * a new data holder for an account. */ public void merge(Account origin) { if (origin == null || origin == this || origin.getServiceId() != getServiceId()) { return; } synchronized (buddyGroupList) { buddyGroupList.clear(); buddyGroupList.addAll(origin.buddyGroupList); } synchronized (noGroupBuddies) { noGroupBuddies.clear(); noGroupBuddies.addAll(origin.noGroupBuddies); } connectionState = origin.connectionState; isEnabled = origin.isEnabled; onlineInfo.merge(origin.getOnlineInfo()); } /** * Get preferences storage file name for an account. * * @return filename */ public String getFilename() { return getAccountId() + ApiConstants.GENERAL_DIVIDER + Utils.escapeGeneralDividers(protocolUid); } /** * Get human-readable account nickname. If Nickname is empty, protocol UID * is returned. * * @return */ public String getSafeName() { return (onlineInfo.getName() != null && onlineInfo.getName().length() > 0) ? onlineInfo.getName() : protocolUid; } public List<Buddy> getBuddyList() { List<Buddy> list = new ArrayList<Buddy>(); synchronized (noGroupBuddies) { list.addAll(noGroupBuddies); } synchronized (buddyGroupList) { for (BuddyGroup g : buddyGroupList) { list.addAll(g.getBuddyList()); } } return list; } /** * Set new buddy list. * * @param buddyList */ public synchronized void setBuddyList(List<BuddyGroup> buddyList) { List<BuddyGroup> old = new ArrayList<BuddyGroup>(); synchronized (buddyGroupList) { old.addAll(this.buddyGroupList); this.buddyGroupList.clear(); this.buddyGroupList.addAll(buddyList); for (Iterator<BuddyGroup> i = this.buddyGroupList.iterator(); i.hasNext();) { boolean done = false; BuddyGroup bg = i.next(); if (bg.getId() == null || bg.getId().equals(ApiConstants.NO_GROUP_ID) || bg.getId().equals(ApiConstants.NOT_IN_LIST_GROUP_ID)) { noGroupBuddies.addAll(bg.getBuddyList()); i.remove(); } for (BuddyGroup obg : old) { if (bg.getId().equals(obg.getId())) { bg.setCollapsed(obg.isCollapsed()); done = true; break; } } if (done) { continue; } } } for (Iterator<String> unreads = unreadsMap.keySet().iterator(); unreads.hasNext();) { String unreadKey = unreads.next(); for (Buddy bu : getBuddyList()) { if (bu.getProtocolUid().equals(unreadKey)) { bu.setUnread((byte) unreadsMap.get(unreadKey)); } } } if (undeletable != null) { noGroupBuddies.addAll(undeletable); undeletable = null; } unreadsMap.clear(); } @Override public String toString() { return getSafeName(); } /** * @return the isEnabled */ public boolean isEnabled() { return isEnabled; } /** * @param isEnabled * the isEnabled to set */ public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } /** * @return the ownName */ public String getOwnName() { return onlineInfo.getName(); } /** * @param ownName * the ownName to set */ public void setOwnName(String ownName) { this.onlineInfo.setName(ownName); } /** * @return the connectionState */ public ConnectionState getConnectionState() { return connectionState; } /** * @param connectionState * the connectionState to set */ public void setConnectionState(ConnectionState connectionState) { this.connectionState = connectionState; } /** * @return the protocolName */ public String getProtocolName() { return protocolName; } /** * @return the protocolServicePackageName */ public String getProtocolServicePackageName() { return protocolServicePackageName; } /** * @return the protocolUid */ public String getProtocolUid() { return protocolUid; } /** * @return the buddyGroupList */ public List<BuddyGroup> getBuddyGroupList() { return buddyGroupList; } /** * @return the onlineInfo */ public OnlineInfo getOnlineInfo() { return onlineInfo; } @Override public String getEntityId() { return getAccountId(); } /** * @return the noGroupBuddies */ public List<Buddy> getNoGroupBuddies() { return noGroupBuddies; } }